/* File: parser.y
 * --------------
 * Bison input file to generate the parser for the compiler.
 *
 * pp2: your job is to write a parser that will construct the parse tree
 *      and if no parse errors were found, print it.  The parser should
 *      accept the language as described in specification, and as augmented
 *      in the pp2 handout.
 */

%{
    
    /* Just like lex, the text within this first region delimited by %{ and %}
     * is assumed to be C/C++ code and will be copied verbatim to the y.tab.c
     * file ahead of the definitions of the yyparse() function. Add other header
     * file inclusions or C++ variable declarations/prototypes that are needed
     * by your code here.
     */
#include "scanner.h" // for yylex
#include "parser.h"
#include "errors.h"
    
    void yyerror(const char *msg
                 ); // standard error-handling routine
    
    %}

/* The section before the first %% is the Definitions section of the yacc
 * input file. Here is where you declare tokens and types, add precedence
 * and associativity options, and so on.
 */

/* yylval
 * ------
 * Here we define the type of the yylval global variable that is used by
 * the scanner to store attibute information about the token just scanned
 * and thus communicate that information to the parser.
 *
 * pp2: You will need to add new fields to this union as you add different
 *      attributes to your non-terminal symbols.
 */
%union {
    int integerConstant;
    bool boolConstant;
    char *stringConstant;
    double doubleConstant;
    char identifier[MaxIdentLen+1]; // +1 for terminating null
    Decl *decl;
    List<Decl*> *declList;
    BreakStmt *breakStmt;
    VarDecl *varDecl;
    VarDecl *var;
    Type *type;
    FnDecl *fnDecl;
    List<VarDecl*> *varList;
    StmtBlock *stmtBlock;
    ForStmt *forStmt;
    WhileStmt *whileStmt;
    List<Expr*> *exprList;
    List<Stmt*> *stmtList;
    List <Identifier*> *identifierList;
    Expr *expr;
    Stmt *stmt;
    NamedType *namedType;
    List<NamedType*> *namedTypeList;
    Call *call;
    ClassDecl *classDecl;
    LValue *lValue;
    
    PrintStmt *printStmt;
    ReturnStmt *returnStmt;

    SwitchStmt *switchStmt;
    Case *caseStmt;
    List<Case*> *caseList;
    Default *defaultStmt;
    
    IfStmt *ifStmt;
    InterfaceDecl *interfaceDecl;
}


/* Tokens
 * ------
 * Here we tell yacc about all the token types that we are using.
 * Bison will assign unique numbers to these and export the #define
 * in the generated y.tab.h header file.
 */
%token   T_Void T_Bool T_Int T_Double T_String T_Class
%token   T_LessEqual T_GreaterEqual T_Equal T_NotEqual T_Dims
%token   T_And T_Or T_Null T_Extends T_This T_Interface T_Implements
%token   T_While T_For T_If T_Else T_Return T_Break
%token   T_New T_NewArray T_Print T_ReadInteger T_ReadLine
%token   T_Increment T_Decrement T_Switch T_Case T_Default

%token   <identifier> T_Identifier
%token   <stringConstant> T_StringConstant
%token   <integerConstant> T_IntConstant
%token   <doubleConstant> T_DoubleConstant
%token   <boolConstant> T_BoolConstant

%nonassoc NoElse
%nonassoc T_Else
%right '='
%right '!'

%left '-'
%left '<'
%left '>'
%left T_Dims
%left T_Or
%left T_And
%left T_Increment
%left T_Decrement
%left T_NotEqual
%left T_Equal
%left '+'
%left '%'
%left '*'
%left '/'
%left '['
%left ']'
%left T_GreaterEqual
%left T_LessEqual
%left  '.'



/* Non-terminal types
 * ------------------
 * In order for yacc to assign/access the correct field of $$, $1, we
 * must to declare which field is appropriate for the non-terminal.
 * As an example, this first type declaration establishes that the DeclList
 * non-terminal uses the field named "declList" in the yylval union. This
 * means that when we are setting $$ for a reduction for DeclList ore reading
 * $n which corresponds to a DeclList nonterminal we are accessing the field
 * of the union named "declList" which is of type List<Decl*>.
 * pp2: You'll need to add many of these of your own.
 */
%type <declList>  DeclList
%type <decl>      Decl

%type <var> Var
%type <expr> Expr
%type <expr> Constant
%type <exprList>  FnList
%type <exprList> ExprList
%type <stmtBlock> StmtBlock
%type <stmt> Stmt
%type <stmtList> StmtList
%type <fnDecl> FnDecl
%type <expr> ExprActual
%type <varList> Parameters
%type <call> Call
%type <varList> VarList
%type <varDecl> VarDecl
%type <varList> VarDeclList
%type <type> Type
%type <stmt> PrintStmt
%type <stmt> ReturnStmt
%type <lValue> LValue;
%type <identifierList> IdentifierList
%type <namedType> IfExtends
%type <namedTypeList> IfImplements
%type <decl> ClassField
%type <declList> ClassFieldList
%type <classDecl> ClassDecl
%type <forStmt> ForStmt
%type <whileStmt> WhileStmt
%type <decl> Prototype
%type <declList> PrototypeList
%type <interfaceDecl> InterfaceDecl

/* Switch Statement Rules */
%type <switchStmt> SwitchStmt
%type <defaultStmt> Default
%type <defaultStmt> DefaultNull
%type <caseStmt> Case
%type <caseList> ListCase
%type <stmtList> ListStatement
%type <breakStmt> BreakStmt

/* If Statement */
%type <ifStmt> IfStmt
%type <stmt> ElseBlock


%%
/* Rules
 * -----
 * All productions and actions should be placed between the start and stop
 * %% markers which delimit the Rules section.
 
 */
Program   :    DeclList            {
    @1;
    /* pp2: The @1 is needed to convince
     * yacc to set up yylloc. You can remove
     * it once you have other uses of @n*/
    Program *program = new Program($1);
    // if no errors, advance to next phase
    if (ReportError::NumErrors() == 0)
        program->Print(0);
        }
;

DeclList  :    DeclList Decl        { ($$=$1)->Append($2); }
|    Decl                 { ($$ = new List<Decl*>)->Append($1); }
;

Decl
:       VarDecl     { $$ = $1; }
|       FnDecl    { $$ = $1; }
|       ClassDecl { $$ = $1; }
|       InterfaceDecl { $$ = $1; }
;

VarDecl
:       Var ';' { $$ = $1; }
|       Var error ';'      {  }
;

VarDeclList
:       VarDeclList VarDecl { $1->Append($2); $$=$1; }
|       VarDecl { $$ = new List<VarDecl*>; $$->Append($1); }
;

FnDecl
:       Type T_Identifier '(' Parameters ')' StmtBlock {
    $$ = new FnDecl(new Identifier(@2,$2), $1 ,$4);
    $$->SetFunctionBody($6);
}
|       Type T_Identifier '(' error ')' StmtBlock {
        $$ = new FormalsError();
}
|       T_Void T_Identifier '(' Parameters ')' StmtBlock {
    $$ = new FnDecl(new Identifier(@2,$2), new Type(*Type::voidType),$4);
    $$->SetFunctionBody($6);
}
|       T_Void T_Identifier '(' error ')' StmtBlock {
        $$ = new FormalsError();
}
;



PrintStmt
:       T_Print '(' ExprList ')' ';' { $$ = new PrintStmt($3); }
;

IfExtends
:       T_Extends T_Identifier  { $$ = new NamedType(new Identifier(@2,$2)); }
|                              { $$ = NULL; }

IfImplements
:       T_Implements IdentifierList { $$ = new List<NamedType*>; 
                for ( int i = 0; i < $2->NumElements(); i++ ) {
                        $$->Append(new NamedType($2->Nth(i)));
                }
        }
|               { $$ = new List<NamedType*>; }
;

WhileStmt
:       T_While '(' Expr ')' Stmt { $$ = new WhileStmt($3,$5); }
;

IfStmt
:       T_If '(' Expr ')' Stmt ElseBlock {$$ = new IfStmt($3, $5, $6); }
|       T_If '(' error ')' Stmt ElseBlock { $$ = new IfStmtExprError(); }
;

ElseBlock
:       T_Else Stmt %prec T_Else {$$ = $2;}
|                   %prec NoElse {$$ = NULL;}
;

ClassDecl
:      T_Class T_Identifier IfExtends IfImplements '{' ClassFieldList '}' { $$ = new ClassDecl(new Identifier(@2,$2),$3,$4,$6); }
;

BreakStmt
:       T_Break ';'     { $$ = new BreakStmt(@1); }
;

ForStmt
:       T_For '(' ExprActual ';' Expr ';' ExprActual ')' Stmt { $$ = new ForStmt($3,$5,$7,$9); }
;

IdentifierList
:       IdentifierList ',' T_Identifier { $$ = $1; $$->Append(new Identifier(@3,$3)); }
|       T_Identifier                    { $$ = new List<Identifier*>;
    $$->Append(new Identifier(@1,$1)); }
;

ExprActual
:       Expr    { $$ = $1;}
|               { $$ = new EmptyExpr; }
;

Call
:       T_Identifier '(' FnList ')' { $$ = new Call(@1,NULL,new Identifier(@1,$1),$3); }
|       T_Identifier '(' error ')'       { $$ = new ActualsError(); }
|       Expr '.' T_Identifier '(' FnList ')' { $$ = new Call(@2,$1,new Identifier(@3,$3),$5); }
|       Expr '.' T_Identifier '(' error ')' { $$ = new ActualsError(); }
;

ClassFieldList
:       ClassFieldList ClassField       { $$ = $1; $$->Append($2); }
|                                       { $$ = new List<Decl*>; }
;

ClassField
:       VarDecl         { $$ = $1; }
|       FnDecl          { $$ = $1; }
;

StmtList
:       StmtList Stmt     { ($$ = $1)->Append($2); }
|       Stmt              { ($$ = new List<Stmt*>)->Append($1); }
|                         { $$ = new List<Stmt*>; }
;

ListStatement
:       ListStatement Stmt { ($$ = $1)->Append($2); }
|       { $$ = new List<Stmt*>; }
;

Stmt
:       ExprActual ';' { $$ = $1; }
|       PrintStmt  { $$ = $1; }
|       ReturnStmt { $$ = $1; }
|       SwitchStmt { $$ = $1; }
|       BreakStmt  { $$ = $1; }
|       ForStmt    { $$ = $1; }
|       WhileStmt  { $$ = $1; }
|       IfStmt     { $$ = $1; }
|       StmtBlock   { $$ = $1; }
;

InterfaceDecl
:       T_Interface T_Identifier '{' PrototypeList '}' { $$ = new InterfaceDecl(new Identifier(@2,$2),$4); }
;

PrototypeList
:       PrototypeList Prototype { $$ = $1; $$->Append($2); }
|           { $$ = new List<Decl*>; }
;

Prototype
:       Type T_Identifier '(' Parameters ')' ';' { $$ = new FnDecl(new Identifier(@2,$2),$1,$4); }
|       Type T_Identifier '(' error ')' ';' {  }
|       T_Void T_Identifier '(' Parameters ')' ';' { $$ = new FnDecl(new Identifier(@2,$2),new Type(*Type::voidType),$4); }
|       T_Void T_Identifier '(' error ')' ';' {  }
;

ReturnStmt
:       T_Return ExprActual ';' { $$ = new ReturnStmt(@1, $2); }
;

StmtBlock
:       '{' VarDeclList StmtList '}'     { $$ = new StmtBlock($2,$3); }        
|       '{' VarDeclList '}'     { $$ = new  StmtBlock($2,new List<Stmt*>); }
|       '{' StmtList '}'     { $$ = new StmtBlock(new List<VarDecl*>,$2); }
|       '{' '}' { List<VarDecl*> *vars = new List<VarDecl*>;
    List<Stmt*> *stmts = new List<Stmt*>;
    $$ = new StmtBlock(vars, stmts);
}
;

Parameters
:       VarList    { $$ = $1; }
|                  { $$ = new List<VarDecl*>; }

VarList
:       VarList ',' Var       { $1->Append($3); $$=$1; }
|       Var        { $$ = new List<VarDecl*>; $$->Append($1); }
;

Var
:       Type T_Identifier    { $$ = new VarDecl( new Identifier(@2, $2), $1 ); }
;

Expr
:       Constant        { $$ = $1; }
|       LValue          { $$ = $1; }
|       T_This          { $$ = new This(@1); }
|       Call            { $$ = $1; }
|       '(' Expr ')'    { $$ = $2; }
|       LValue '=' Expr { $$ = new AssignExpr($1,new Operator(@2,"="),$3); }
|       '-' Expr        {$$ = new ArithmeticExpr(new Operator(@1, "-"), $2);}
|       Expr '%' Expr   { $$ = new ArithmeticExpr($1, new Operator(@2, "%"), $3); }
|       Expr '<' Expr        { $$ = new RelationalExpr($1,new Operator(@2,"<"),$3); }
|       Expr T_LessEqual Expr        { $$ = new RelationalExpr($1,new Operator(@2,"<="),$3); }
|       Expr '>' Expr        { $$ = new RelationalExpr($1,new Operator(@2,">"),$3); }
|       Expr T_GreaterEqual Expr        { $$ = new RelationalExpr($1,new Operator(@2,">="),$3); }
|       Expr T_Equal Expr       { $$ = new EqualityExpr($1,new Operator(@2,"=="),$3); }
|       Expr '+' Expr   { $$ = new ArithmeticExpr($1, new Operator(@2, "+"), $3); }
|       Expr '-' Expr   { $$ = new ArithmeticExpr($1, new Operator(@2, "-"), $3); }
|       Expr T_NotEqual Expr    { $$ = new EqualityExpr($1,new Operator(@2,"!="),$3); }
|       Expr T_And Expr         { $$ = new LogicalExpr($1,new Operator(@2,"&&"),$3); }
|       Expr '/' Expr   { $$ = new ArithmeticExpr($1, new Operator(@2, "/"), $3); }
|       Expr '*' Expr   { $$ = new ArithmeticExpr($1, new Operator(@2, "*"), $3); }
|       Expr T_Or Expr         { $$ = new LogicalExpr($1,new Operator(@2,"||"),$3); }
|       LValue T_Increment      { $$ = new PostfixExpr($1,new Operator(@2,"++")); }
|       LValue T_Decrement      { $$ = new PostfixExpr($1,new Operator(@2,"--")); }
|       '!' Expr        { $$ = new LogicalExpr(new Operator(@1,"!"),$2); }
|       T_ReadInteger '(' ')'   { $$ = new ReadIntegerExpr(@1); }
|       T_NewArray    '(' Expr ',' Type ')'   { $$ = new NewArrayExpr(@1,$3,$5); }
|       T_New T_Identifier      { $$ = new NewExpr(@1, new NamedType(new Identifier(@2,$2)));}
|       T_ReadLine '(' ')'  { $$ = new ReadLineExpr(@1); }
;

LValue
:       T_Identifier            { $$ = new FieldAccess(NULL, new Identifier(@1, $1)); }
|       Expr '[' Expr ']'       { $$ = new ArrayAccess(@1,$1,$3); }
|       Expr '.' T_Identifier   { $$ = new FieldAccess($1,new Identifier(@3,$3)); }
;

Constant
:       T_Null          { $$ = new NullConstant(@1); }
|       T_IntConstant   { $$ = new IntConstant(@1,$1); }
|       T_BoolConstant  { $$ = new BoolConstant(@1,$1); }
|       T_StringConstant        { $$ = new StringConstant(@1,$1); }
|       T_DoubleConstant        { $$ = new DoubleConstant(@1,$1); }
;

FnList
:       ExprList {$$ = $1;}
|       {$$ = new List<Expr*>;}
;

ExprList
:       ExprList ',' Expr { $1->Append($3); $$=$1; }
|       Expr    { $$ = new List<Expr*>; $$->Append($1); }
;

SwitchStmt
:       T_Switch '(' Expr ')' '{' ListCase DefaultNull '}' { $$ = new SwitchStmt($3, $6, $7); }
;

Case
:    T_Case T_IntConstant ':' ListStatement { $$ = new Case(new IntConstant(@2, $2), $4); }
;

Default
:       T_Default ':' ListStatement {$$ = new Default($3); }
;

ListCase
:       Case { ($$ = new List<Case*>)->Append($1); }
|       ListCase Case { ($$ = $1)->Append($2); }
;

DefaultNull
:       Default { $$ = $1; }
|       { $$ = NULL; }   
;

Type
:       T_Identifier    { $$ = new NamedType(new Identifier(@1,$1)); }
|       Type T_Dims     { $$ = new ArrayType(@1,$1); }
|       T_Int           { $$ = new Type(*Type::intType); }
|       T_Double        { $$ = new Type(*Type::doubleType); }
|       T_String        { $$ = new Type(*Type::stringType); }
|       T_Bool          { $$ = new Type(*Type::boolType); }
;

%%

/* The closing %% above marks the end of the Rules section and the beginning
 * of the User Subroutines section. All text from here to the end of the
 * file is copied verbatim to the end of the generated y.tab.c file.
 * This section is where you put definitions of helper functions.
 */

/* Function: InitParser
 * --------------------
 * This function will be called before any calls to yyparse().  It is designed
 * to give you an opportunity to do anything that must be done to initialize
 * the parser (set global variables, configure starting state, etc.). One
 * thing it already does for you is assign the value of the global variable
 * yydebug that controls whether yacc prints debugging information about
 * parser actions (shift/reduce) and contents of state stack during parser.
 * If set to false, no information is printed. Setting it to true will give
 * you a running trail that might be helpful when debugging your parser.
 * Please be sure the variable is set to false when submitting your final
 * version.
 */
void InitParser()
{
    PrintDebug("parser", "Initializing parser");
    yydebug = false;
}